﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Xaml;
using System.Xml;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Baml2006;
using System.Reflection;
using Kokomo.Mirror.CliMetadata;
using System.Text.RegularExpressions;
using System.Xaml.Schema;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;

namespace Kokomo.PeInspector.Reflection
{
	class XamlDocumentBuilder : XmlWriter,
		System.Windows.Markup.IXamlTypeResolver,
		IXamlNamespaceResolver,
		IXamlSchemaContextProvider,
		IAmbientProvider
	{
		private const string xKeySurrogate = "x_Key";

		static XamlDocumentBuilder()
		{
			Dictionary<char, string> contentCharEntityTable = new Dictionary<char, string>();
			contentCharEntityTable['&'] = "amp";
			contentCharEntityTable['<'] = "lt";
			contentCharEntityTable['>'] = "gt";
			_contentCharEntityTable = contentCharEntityTable;

			Dictionary<char, string> attributeCharEntityTable = new Dictionary<char, string>();
			attributeCharEntityTable['&'] = "amp";
			attributeCharEntityTable['<'] = "lt";
			attributeCharEntityTable['>'] = "gt";
			attributeCharEntityTable['\"'] = "quot";
			attributeCharEntityTable['\''] = "apos";
			_attributeCharEntityTable = attributeCharEntityTable;
		}

		static Dictionary<char, string> _contentCharEntityTable;
		static Dictionary<char, string> _attributeCharEntityTable;

		public static void Build(
			XamlReader xamlReader,
			ICodeWriter writer,
			Assembly primaryAssembly,
			IAssemblyLoadContext loadContext
			)
		{
			if (xamlReader == null) throw new ArgumentNullException("xamlReader");
			if (writer == null) throw new ArgumentNullException("writer");

			XamlDocumentBuilder builder = new XamlDocumentBuilder(writer, loadContext, primaryAssembly);

			using (XamlXmlWriter xamlWriter = new XamlXmlWriter(builder, builder.SchemaContext))
			{
				builder.Build(xamlReader, xamlWriter);
				xamlWriter.Flush();
			}
		}
		private void Build(XamlReader xamlReader, XamlXmlWriter xamlWriter, IList keys = null)
		{
			xamlWriter.Settings.AssumeValidInput = true;

			int depth = 0;
			int rootRecordIndex = 0;

			while (xamlReader.Read())
			{
				//xamlWriter.WriteNode(xamlReader);
				//continue;

				switch (xamlReader.NodeType)
				{
					case XamlNodeType.EndMember:
						try
						{
							xamlWriter.WriteEndMember();
						}
						catch
						{
							xamlWriter.WriteValue(null);
							xamlWriter.WriteEndMember();
						}
						//builder.WriteEndMember();
						break;
					case XamlNodeType.EndObject:
						xamlWriter.WriteEndObject();
						depth--;
						//builder.WriteEndObject();
						break;
					case XamlNodeType.GetObject:
						xamlWriter.WriteGetObject();
						depth++;
						break;
					case XamlNodeType.NamespaceDeclaration:
						NamespaceDeclaration namespaceDeclaration = xamlReader.Namespace;
						if (string.IsNullOrEmpty(namespaceDeclaration.Prefix))
							this._defaultNamespace = namespaceDeclaration.Namespace;
						else
							this._namespacePrefixTable[namespaceDeclaration.Prefix] = namespaceDeclaration.Namespace;

						this._namespaces.Add(namespaceDeclaration);
						//builder.AddNamespace(namespaceDeclaration);
						//string preferredPrefix;
						//bool namespaceExists = xamlWriter.SchemaContext.TryGetCompatibleXamlNamespace(namespaceDeclaration.Namespace, out preferredPrefix);
						//if (preferredPrefix == null)
						{
							try
							{
								xamlWriter.WriteNamespace(namespaceDeclaration);
							}
							catch { }
						}
						break;
					case XamlNodeType.None:
						break;
					case XamlNodeType.StartMember:
						//builder.WriteStartMember(xamlReader.Member);
						xamlWriter.WriteStartMember(xamlReader.Member);
						break;
					case XamlNodeType.StartObject:
						//builder.WriteStartObject(xamlReader.Type);
						xamlWriter.WriteStartObject(xamlReader.Type);
						if (depth == 0)
						{
							if (keys != null)
							{
								object key = keys[rootRecordIndex];
								XamlMember keyMember = new XamlMember(xKeySurrogate, xamlReader.Type, false);
								xamlWriter.WriteStartMember(keyMember);
								string keyString = (string)key.GetType().GetProperty("KeyString").GetValue(key, null);
								this.WriteValue(keyString, xamlReader.SchemaContext, xamlWriter);
								xamlWriter.WriteEndMember();
							}
							rootRecordIndex++;
						}
						depth++;
						break;
					case XamlNodeType.Value:
						//builder.WriteValue(xamlReader.Value);
						{
							this.WriteValue(xamlReader.Value, xamlReader.SchemaContext, xamlWriter);
						}
						break;
					default:
						break;
				}
			}
			//string xml = textWriter.GetStringBuilder().ToString();
			//XmlDocument document = new XmlDocument();
			//document.LoadXml(xml);
		}

		#region Write methods
		private void WriteValue(object value, XamlSchemaContext schemaContext, XamlXmlWriter xamlWriter)
		{
			string text = value as string;
			if (text == null)
			{
				Stream stream = value as Stream;
				if (stream != null)
				{
					this.WriteStream(stream, schemaContext, xamlWriter);

					//byte[] buffer = new byte[stream.Length];
					//stream.Read(buffer, 0, buffer.Length);
					//value = Convert.ToBase64String(buffer);
				}
				else
				{
					Type type = value as Type;
					if (type != null)
					{
						this.WriteType(type, xamlWriter);
					}
					else
					{
						if (value == null)
						{
							this.WriteNull(xamlWriter);
						}
						else
						{
							XamlNodeList nodeList = value as XamlNodeList;
							if (nodeList != null)
							{
								this.WriteNodeList(nodeList, xamlWriter);
							}
							else
							{
								DependencyProperty property = value as DependencyProperty;
								if (property != null)
								{
									text = property.Name;
								}
								else
								{
									StaticResourceExtension staticResourceExtension = value as StaticResourceExtension;
									if (staticResourceExtension != null)
									{
										this.WriteStaticResourceExtension(staticResourceExtension, xamlWriter);
									}
									else
									{
										DynamicResourceExtension dynamicResourceExtension = value as DynamicResourceExtension;
										if (dynamicResourceExtension != null)
										{
											this.WriteDynamicResourceExtension(dynamicResourceExtension, xamlWriter);
										}
										else
										{
											Type valueType = value.GetType();

											if (valueType.FullName == "System.Windows.Baml2006.TypeConverterMarkupExtension")
											{
												text = (string)valueType.GetField("_value", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(value);
											}
											else
											{
												if (valueType.FullName == "System.Windows.Baml2006.DeferredBinaryDeserializerExtension")
												{
													System.Windows.Markup.MarkupExtension markupExtension = value as System.Windows.Markup.MarkupExtension;
													object extensionValue = markupExtension.ProvideValue(this.ServiceProvider);
													if (extensionValue != null)
													{
														TypeConverter converter = TypeDescriptor.GetConverter(extensionValue);
														if (
															(converter != null)
															&& (converter.CanConvertTo(typeof(string)))
															)
														{
															//new XamlXmlReaderSettings() { 
															//XamlObjectReader objectReader = new XamlObjectReader(markupExtension, this.SchemaContext, new XamlObjectReaderSettings() { );
															//this.Build(objectReader, xamlWriter);
															try
															{
																text = converter.ConvertTo(extensionValue, typeof(string)) as string;
															}
															catch { /* Fall through to next block */ }
														}
													}
												}
												else
												{
													text = value.ToString();
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			if (text != null)
			{
				xamlWriter.WriteValue(text);
			}
			else if (value is Type) { }
			else if (value is XamlNodeList) { }
			else { }
		}

		private void WriteStaticResourceExtension(StaticResourceExtension staticResourceExtension, XamlXmlWriter xamlWriter)
		{
			xamlWriter.WriteStartObject(new XamlType(typeof(StaticResourceExtension), this.SchemaContext));
			xamlWriter.WriteStartMember(new XamlMember(typeof(StaticResourceExtension).GetProperty("ResourceKey"), this.SchemaContext));
			this.WriteValue(staticResourceExtension.ResourceKey);
			//xamlWriter.WriteValue(staticResourceExtension.ResourceKey.ToString());
			xamlWriter.WriteEndMember();
			xamlWriter.WriteEndObject();
		}

		private void WriteDynamicResourceExtension(DynamicResourceExtension dynamicResourceExtension, XamlXmlWriter xamlWriter)
		{
			xamlWriter.WriteStartObject(new XamlType(typeof(DynamicResourceExtension), this.SchemaContext));
			xamlWriter.WriteStartMember(new XamlMember(typeof(DynamicResourceExtension).GetProperty("ResourceKey"), this.SchemaContext));
			this.WriteValue(dynamicResourceExtension.ResourceKey);
			//xamlWriter.WriteValue(staticResourceExtension.ResourceKey.ToString());
			xamlWriter.WriteEndMember();
			xamlWriter.WriteEndObject();
		}

		private void WriteNodeList(XamlNodeList nodeList, XamlXmlWriter xamlWriter)
		{
			this.Build(nodeList.GetReader(), xamlWriter);
		}

		private void WriteNull(XamlXmlWriter xamlWriter)
		{
			xamlWriter.WriteStartObject(new XamlType(typeof(System.Windows.Markup.NullExtension), this.SchemaContext));
			xamlWriter.WriteEndObject();
		}

		private void WriteType(Type type, XamlXmlWriter xamlWriter)
		{
			xamlWriter.WriteStartObject(new XamlType(typeof(System.Windows.Markup.TypeExtension), this.SchemaContext));
			xamlWriter.WriteStartMember(new XamlMember(typeof(System.Windows.Markup.TypeExtension).GetProperty("TypeName"), this.SchemaContext));

			XamlType xamlType = new XamlType(type, this.SchemaContext);
			string prefix = this.LookupPrefix(xamlType.PreferredXamlNamespace);

			xamlWriter.WriteValue(type.FullName);
			xamlWriter.WriteEndMember();
			xamlWriter.WriteEndObject();
		}

		private void WriteStream(Stream stream, XamlSchemaContext schemaContext, XamlXmlWriter xamlWriter)
		{
			IList keys;
			XamlReader nestedReader = this.CreateNestedBamlReader(stream, schemaContext, out keys);
			this.Build(nestedReader, xamlWriter, keys);
		}
		#endregion

		#region BAML reader stuff
		private static Type Baml2006SchemaContextType
		{
			get { return typeof(Window).Assembly.GetType("System.Windows.Baml2006.Baml2006SchemaContext", false, false); }
		}
		private static Type Baml2006ReaderSettingsType
		{
			get { return typeof(Window).Assembly.GetType("System.Windows.Baml2006.Baml2006ReaderSettings", false, false); }
		}
		private static XamlSchemaContext CreateBamlSchemaContext(Assembly assembly)
		{
			ConstructorInfo bamlSchemaConstructor = Baml2006SchemaContextType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(Assembly) }, null);
			XamlSchemaContext bamlSchema = (XamlSchemaContext)bamlSchemaConstructor.Invoke(new object[] { assembly });
			return bamlSchema;
		}
		private static XamlReaderSettings CreateBamlSettings()
		{
			ConstructorInfo bamlSettingsConstructor = Baml2006ReaderSettingsType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
			XamlReaderSettings bamlSettings = (XamlReaderSettings)bamlSettingsConstructor.Invoke(new object[0]);
			return bamlSettings;
		}
		private static XamlReaderSettings CreateBamlSettings(XamlReaderSettings settings)
		{
			ConstructorInfo bamlSettingsConstructor = Baml2006ReaderSettingsType.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { settings.GetType() }, null);
			XamlReaderSettings bamlSettings = (XamlReaderSettings)bamlSettingsConstructor.Invoke(new object[] { settings });
			return bamlSettings;
		}
		private static Baml2006Reader CreateBamlReader(
			Stream stream,
			XamlSchemaContext schemaContext,
			XamlReaderSettings settings
			)
		{
			ConstructorInfo bamlReaderConstructor = typeof(Baml2006Reader).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Stream), Baml2006SchemaContextType, Baml2006ReaderSettingsType }, null);
			Baml2006Reader reader = (Baml2006Reader)bamlReaderConstructor.Invoke(new object[] { stream, schemaContext, settings });

			return reader;
		}

		public static Baml2006Reader CreateThemeBamlReader(Stream stream, Assembly localAssembly)
		{
			XamlReaderSettings settings = CreateBamlSettings();
			settings.GetType().GetProperty("LocalAssembly", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).SetValue(settings, localAssembly, null);
			settings.GetType().GetProperty("OwnsStream", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).SetValue(settings, true, null);
			XamlSchemaContext schemaContext = CreateBamlSchemaContext(localAssembly);
			Baml2006Reader reader = CreateBamlReader(stream, schemaContext, settings);

			return reader;
		}
		private XamlReader CreateNestedBamlReader(Stream stream, XamlSchemaContext schemaContext, out IList keys)
		{
			// HACK: Modeled after ResourceDictionary.SetDeferrableContent
			Type bamlSchemaContextType = Baml2006SchemaContextType;
			XamlSchemaContext bamlSchema = schemaContext;// = Activator.CreateInstance(bamlSchemaContextType, BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { this.PrimaryAssembly, this.SchemaContext }, null);

			Type bamlSettingsType = Baml2006ReaderSettingsType;

			XamlReaderSettings bamlSchemaSettings = (XamlReaderSettings)bamlSchema.GetType().GetProperty("Settings", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).GetValue(bamlSchema, null);
			XamlReaderSettings bamlSettings = CreateBamlSettings(bamlSchemaSettings);
			bamlSettingsType.GetProperty("IsBamlFragment", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(bamlSettings, true, null);
			bamlSettingsType.GetProperty("OwnsStream", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(bamlSettings, true, null);
			bamlSettingsType.GetProperty("LocalAssembly", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).SetValue(bamlSettings, this.PrimaryAssembly, null);

			//ConstructorInfo bamlReaderConstructor = bamlReaderType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Stream), bamlSchemaContextType, bamlSettingsType }, null);
			Baml2006Reader reader = CreateBamlReader(stream, bamlSchema, bamlSettings);

			// HACK: Prime the reader
			keys = (IList)reader.GetType().GetMethod("ReadKeys", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(reader, new object[0]);

			return reader;
		}
		#endregion

		public XamlDocumentBuilder(
			ICodeWriter writer,
			IAssemblyLoadContext loadContext,
			Assembly primaryAssembly
			)
		{
			this.Writer = writer;

			this._writeState = System.Xml.WriteState.Start;

			this._namespaceTable = new Dictionary<string, string>();
			this._namespacePrefixTable = new Dictionary<string, string>();
			this._namespaces = new List<NamespaceDeclaration>();
			this._elementContextStack = new Stack<XmlWriterElementContext>();
			this.PrimaryAssembly = primaryAssembly;
			this.LoadContext = loadContext;
			this.SchemaContext = new XamlSchemaContext();

			ServiceContainer container = new ServiceContainer();
			this.ServiceProvider = container;
			container.AddService(typeof(System.Windows.Markup.IXamlTypeResolver), this);
			container.AddService(typeof(IXamlNamespaceResolver), this);
			container.AddService(typeof(IXamlSchemaContextProvider), this);
			container.AddService(typeof(IAmbientProvider), this);
		}

		public ICodeWriter Writer { get; private set; }
		public IAssemblyLoadContext LoadContext { get; private set; }
		public Assembly PrimaryAssembly { get; private set; }
		public XamlSchemaContext SchemaContext { get; private set; }
		public IServiceProvider ServiceProvider { get; private set; }

		private Dictionary<string, string> _namespaceTable;
		private Dictionary<string, string> _namespacePrefixTable;
		private List<NamespaceDeclaration> _namespaces;

		private string _defaultNamespace;

		private Stack<XmlWriterElementContext> _elementContextStack;
		private XmlWriterElementContext CurrentElementContext
		{
			get
			{
				XmlWriterElementContext context =
					(this._elementContextStack.Count > 0)
					? this._elementContextStack.Peek()
					: null
					;

				return context;
			}
		}

		public override void Close()
		{
			this.WriteEndDocument();
			this._writeState = WriteState.Closed;
			// Do nothing
		}

		public override void Flush()
		{
			// Do nothing
		}

		protected void ValidateState(WriteState validState)
		{
			WriteState currentState = this.WriteState;

			if (currentState == validState) return;

			bool isValid = false;
			switch (currentState)
			{
				case WriteState.Attribute:
					break;
				case WriteState.Closed:
					break;
				case WriteState.Content:
					break;
				case WriteState.Element:
					switch (validState)
					{
						case WriteState.Content:
							this.Writer.WriteText(">", CodeTextType.Operator);
							isValid = true;
							break;
					}
					break;
				case WriteState.Error:
					break;
				case WriteState.Prolog:
					break;
				case WriteState.Start:
					switch (validState)
					{
						case WriteState.Content:
							isValid = true;
							break;
					}
					break;
				default:
					break;
			}

			if (isValid)
			{
				this._writeState = validState;
				return;
			}

			this._writeState = System.Xml.WriteState.Error;
			throw new InvalidOperationException("Invalid writer state");
		}

		public override string LookupPrefix(string ns)
		{
			if (string.IsNullOrEmpty(ns)) throw new ArgumentNullException("ns");

			string prefix;
			this._namespaceTable.TryGetValue(ns, out prefix);
			return prefix;
		}

		public override void WriteBase64(byte[] buffer, int index, int count)
		{
			if (buffer == null) throw new ArgumentNullException("buffer");

			string base64 = Convert.ToBase64String(buffer, index, count, Base64FormattingOptions.None);
			this.Writer.WriteText(base64, this.CodeTextType);
		}

		public override void WriteCData(string text)
		{
			this.ValidateState(System.Xml.WriteState.Content);

			this.Writer.WriteText("<![CDATA[", Reflection.CodeTextType.Operator);
			if (!string.IsNullOrEmpty(text))
			{
				this.Writer.WriteText(text);
			}
			this.Writer.WriteText("]]>", Reflection.CodeTextType.Operator);
		}

		public override void WriteCharEntity(char ch)
		{
			string entityCode = "#" + ((int)ch).ToString("X");
			this.WriteEntityRef(entityCode);
		}

		public override void WriteChars(char[] buffer, int index, int count)
		{
			if (buffer == null) throw new ArgumentNullException("buffer");
			if (index >= buffer.Length) throw new ArgumentOutOfRangeException("index");
			if ((index + count) > buffer.Length) throw new ArgumentOutOfRangeException("count");

			StringBuilder textBuilder = new StringBuilder(count);
			for (int i = 0; i < count; i++)
			{
				char c = buffer[i + index];

				textBuilder.Append(c);
			}

			string text = textBuilder.ToString();
			this.Writer.WriteText(text, this.CodeTextType);
		}

		public override void WriteComment(string text)
		{
			this.ValidateState(WriteState.Content);

			this.Writer.WriteText("<!--", CodeTextType.Operator);
			this.Writer.WriteText(text, CodeTextType.Comment);
			this.Writer.WriteText("-->", CodeTextType.Operator);
		}

		public override void WriteDocType(string name, string pubid, string sysid, string subset)
		{
			if (name == null) throw new ArgumentNullException("name");
			this.ValidateState(WriteState.Start);

			this.Writer.WriteText("<!DOCTYPE ", Reflection.CodeTextType.Operator);
			this.Writer.WriteText(name);
			if (!string.IsNullOrEmpty(pubid))
			{
				this.Writer.WriteText(" PUBLIC ", Reflection.CodeTextType.XmlAttributeName);
				this.Writer.WriteText("\"", Reflection.CodeTextType.Operator);
				this.Writer.WriteText(pubid, Reflection.CodeTextType.XmlAttributeValue);
				this.Writer.WriteText("\"", Reflection.CodeTextType.Operator);
			}
			if (!string.IsNullOrEmpty(sysid))
			{
				this.Writer.WriteText(" SYSTEM ", Reflection.CodeTextType.XmlAttributeName);
				this.Writer.WriteText("\"", Reflection.CodeTextType.Operator);
				this.Writer.WriteText(sysid, Reflection.CodeTextType.XmlAttributeValue);
				this.Writer.WriteText("\"", Reflection.CodeTextType.Operator);
			}

			if (!string.IsNullOrEmpty(subset))
			{
				this.Writer.WriteText("[", Reflection.CodeTextType.Operator);
				this.Writer.WriteText(subset);
				this.Writer.WriteText("]", Reflection.CodeTextType.Operator);
			}

			this.Writer.WriteText(">", Reflection.CodeTextType.Operator);

			this._writeState = System.Xml.WriteState.Prolog;
		}

		public override void WriteEndAttribute()
		{
			this.ValidateState(WriteState.Attribute);

			this.Writer.WriteText("\"", Reflection.CodeTextType.Operator);
			this._writeState = System.Xml.WriteState.Element;
		}

		public override void WriteEndDocument()
		{
			this.ValidateState(WriteState.Content);

			while (this._elementContextStack.Count > 0)
			{
				this.WriteEndElement();
			}
		}

		public override void WriteEndElement()
		{
			if (this._elementContextStack.Count == 0) throw new InvalidOperationException("Invalid writer state");

			this.Writer.Outdent();
			XmlWriterElementContext elementContext = this._elementContextStack.Pop();

			if (this.WriteState == System.Xml.WriteState.Element)
			{
				this.Writer.WriteText("/>", Reflection.CodeTextType.Operator);
				this.Writer.WriteLineBreak();
			}
			else
			{
				this.ValidateState(System.Xml.WriteState.Content);

				this.Writer.WriteText("</", Reflection.CodeTextType.Operator);
				if (!string.IsNullOrEmpty(elementContext.Prefix))
				{
					this.Writer.WriteText(elementContext.Prefix, Reflection.CodeTextType.XmlElementName);
					this.Writer.WriteText(":", Reflection.CodeTextType.Operator);
				}
				this.Writer.WriteText(elementContext.ElementName, Reflection.CodeTextType.XmlElementName);
				this.Writer.WriteText(">", Reflection.CodeTextType.Operator);
				this.Writer.WriteLineBreak();
			}

			this._writeState = System.Xml.WriteState.Content;
		}

		public override void WriteEntityRef(string name)
		{
			this.Writer.WriteText("&", Reflection.CodeTextType.Operator);
			this.Writer.WriteText(name, Reflection.CodeTextType.XmlEntityName);
			this.Writer.WriteText(";", Reflection.CodeTextType.Operator);
		}

		public override void WriteFullEndElement()
		{
			this.ValidateState(System.Xml.WriteState.Content);
			this.WriteEndElement();
		}

		public override void WriteProcessingInstruction(string name, string text)
		{
			this.ValidateState(System.Xml.WriteState.Content);
		}

		public override void WriteRaw(string data)
		{
			this.Writer.WriteText(data, this.CodeTextType);
		}

		public override void WriteRaw(char[] buffer, int index, int count)
		{
			this.WriteChars(buffer, index, count);
		}

		public override void WriteStartAttribute(string prefix, string localName, string ns)
		{
			this.ValidateState(System.Xml.WriteState.Element);

			if (localName == xKeySurrogate)
			{
				localName = "Key";
				prefix = "x";
			}

			//this.Writer.WriteText(" ");
			XmlWriterElementContext context = this.CurrentElementContext;
			if (context.HasAttributes)
			{
				this.Writer.WriteLineBreak();
			}
			else
			{
				this.Writer.WriteText(" ");
				context.HasAttributes = true;
			}

			if (!string.IsNullOrEmpty(prefix))
			{
				this.Writer.WriteText(prefix, Reflection.CodeTextType.XmlAttributeName);
				if (!string.IsNullOrEmpty(localName))
				{
					this.Writer.WriteText(":", Reflection.CodeTextType.Operator);
				}
			}

			string[] nameParts = localName.Split('.');
			if (nameParts.Length >= 1)
			{
				Type type = null;
				string memberName;
				if (nameParts.Length == 2)
				{
					string typeName = nameParts[0];
					memberName = nameParts[1];
					XamlType xamlType = this.SchemaContext.GetXamlType(new XamlTypeName(ns ?? this._defaultNamespace, typeName));
					type = (xamlType == null)
						? null
						: xamlType.UnderlyingType
						;
					if (type != null)
						this.Writer.WriteMemberReference(type, typeName, Reflection.CodeTextType.XmlAttributeName);
					else
						this.Writer.WriteText(typeName, Reflection.CodeTextType.XmlAttributeName);

					this.Writer.WriteText(".", Reflection.CodeTextType.XmlAttributeName);
				}
				else
				{
					type = context.Type;
					memberName = nameParts[0];
				}

				if (type != null)
				{
					MemberInfo member = this.ResolveMember(memberName, type);
					if (member != null)
						this.Writer.WriteMemberReference(member, memberName, Reflection.CodeTextType.XmlAttributeName);
					else
						this.Writer.WriteText(memberName, Reflection.CodeTextType.XmlAttributeName);
				}
				else
				{
					this.Writer.WriteText(memberName, Reflection.CodeTextType.XmlAttributeName);
				}
			}
			else
			{
				this.Writer.WriteText(localName, Reflection.CodeTextType.XmlAttributeName);
			}

			this.Writer.WriteText("=\"", Reflection.CodeTextType.Operator);

			this._writeState = System.Xml.WriteState.Attribute;
		}

		public override void WriteStartDocument(bool standalone)
		{
			if (this.WriteState != System.Xml.WriteState.Start) throw new InvalidOperationException();

			this.Writer.WriteText("<?", Reflection.CodeTextType.Operator);
			this.Writer.WriteText("xml", Reflection.CodeTextType.XmlElementName);
			this.Writer.WriteText(" version", Reflection.CodeTextType.XmlAttributeName);
			this.Writer.WriteText("=\"", Reflection.CodeTextType.Operator);
			this.Writer.WriteText("1.0", Reflection.CodeTextType.XmlAttributeValue);
			this.Writer.WriteText("\"", Reflection.CodeTextType.Operator);
			this.Writer.WriteText(" standalone", Reflection.CodeTextType.XmlAttributeName);
			this.Writer.WriteText("=\"", Reflection.CodeTextType.Operator);
			this.Writer.WriteText(standalone ? "yes" : "no", Reflection.CodeTextType.XmlAttributeValue);
			this.Writer.WriteText("\"?>", Reflection.CodeTextType.Operator);

			this.Writer.WriteLineBreak();

			this._writeState = System.Xml.WriteState.Prolog;
		}

		public override void WriteStartDocument()
		{
			this.WriteStartDocument(true);
		}

		public override void WriteStartElement(string prefix, string localName, string ns)
		{
			bool inlineElement = this.WriteState == System.Xml.WriteState.Content;
			this.ValidateState(System.Xml.WriteState.Content);

			if (!inlineElement)
			{
				this.Writer.WriteLineBreak();
			}

			XmlWriterElementContext context = new XmlWriterElementContext(localName, ns, prefix);

			this.Writer.WriteText("<", Reflection.CodeTextType.Operator);
			if (!string.IsNullOrEmpty(prefix))
			{
				this.Writer.WriteText(prefix, Reflection.CodeTextType.XmlElementName);
				this.Writer.WriteText(":", Reflection.CodeTextType.Operator);
			}

			string[] nameParts = localName.Split('.');
			if (nameParts.Length >= 1)
			{
				string typeName = nameParts[0];
				XamlType xamlType = this.SchemaContext.GetXamlType(new XamlTypeName(ns ?? this._defaultNamespace, typeName));
				Type type = (xamlType == null)
					? null
					: xamlType.UnderlyingType
					;

				if (type != null)
				{
					context.Type = type;
					this.Writer.WriteMemberReference(type, typeName, Reflection.CodeTextType.XmlElementName);
					if (nameParts.Length >= 2)
					{
						this.Writer.WriteText(".", Reflection.CodeTextType.XmlElementName);

						string memberName = nameParts[1];
						MemberInfo member = this.ResolveMember(memberName, type);
						if (member != null)
							this.Writer.WriteMemberReference(member, memberName, Reflection.CodeTextType.XmlElementName);
						else
							this.Writer.WriteText(memberName, Reflection.CodeTextType.XmlElementName);
					}
				}
				else
				{
					this.Writer.WriteText(localName, Reflection.CodeTextType.XmlElementName);
				}
			}
			else
			{
				this.Writer.WriteText(localName, Reflection.CodeTextType.XmlElementName);
			}

			this._writeState = System.Xml.WriteState.Element;

			this._elementContextStack.Push(context);

			this.Writer.Indent();
		}

		//private Type ResolveType(string localName, string ns)
		//{
		//    Match match = Regex.Match(ns, @"clr-namespace:(?<namespace>(\w|\.)+)(;assembly=(?<assembly>(\w|\.)+))?");
		//    if (!match.Success) return null;

		//    string clrNamespace = match.Groups["namespace"].Value;
		//    string assemblyName = match.Groups["assembly"].Value;
		//    Assembly assembly =
		//        (string.IsNullOrEmpty(assemblyName))
		//        ? this.PrimaryAssembly
		//        : this.LoadContext.LoadAssembly(new AssemblyName(assemblyName), this.PrimaryAssembly)
		//        ;
		//    if (assembly == null) return null;

		//    string fullTypeName = clrNamespace + "." + localName;
		//    Type type = assembly.GetType(fullTypeName, false);
		//    return type;
		//}

		private MemberInfo ResolveMember(string memberName, Type type)
		{
			PropertyInfo property = type.GetProperty(memberName);
			return property;
		}

		private WriteState _writeState;
		public override WriteState WriteState
		{
			get { return this._writeState; }
		}

		private CodeTextType CodeTextType
		{
			get
			{
				CodeTextType textType;
				switch (this.WriteState)
				{
					case System.Xml.WriteState.Attribute:
						textType = Reflection.CodeTextType.XmlAttributeValue;
						break;
					default:
						textType = Reflection.CodeTextType.Normal;
						break;
				}

				return textType;
			}
		}

		public override void WriteString(string text)
		{
			if (text == null) throw new ArgumentNullException("text");

			Dictionary<char, string> charEntityTable;
			if (this._writeState == System.Xml.WriteState.Attribute)
			{
				charEntityTable = _attributeCharEntityTable;
			}
			else
			{
				this.ValidateState(System.Xml.WriteState.Content);
				charEntityTable = _contentCharEntityTable;
			}

			StringBuilder textBuilder = new StringBuilder();
			foreach (var c in text)
			{
				string entityName;
				if (charEntityTable.TryGetValue(c, out entityName))
				{
					this.Writer.WriteText(textBuilder.ToString(), this.CodeTextType);
					textBuilder.Clear();

					this.WriteEntityRef(entityName);
				}
				else if (
					(c >= 0)
					&& (c <= 0x1F)
					)
				{
					this.Writer.WriteText(textBuilder.ToString(), this.CodeTextType);
					textBuilder.Clear();

					switch (c)
					{
						case '\x09':
						case '\x0A':
						case '\x0D':
							break;
						default:
							this.WriteCharEntity(c);
							break;
					}
				}
				else
				{
					textBuilder.Append(c);
				}
			}
			this.Writer.WriteText(textBuilder.ToString(), this.CodeTextType);
			textBuilder.Clear();
		}

		public override void WriteSurrogateCharEntity(char lowChar, char highChar)
		{
			throw new NotImplementedException();
		}

		public override void WriteWhitespace(string ws)
		{
			this.Writer.WriteText(ws);
		}

		public Type Resolve(string qualifiedTypeName)
		{
			XamlTypeName name = XamlTypeName.Parse(qualifiedTypeName, this);
			XamlType xamlType = this.SchemaContext.GetXamlType(name);
			Type type = xamlType.UnderlyingType;

			return type;
		}

		public string GetNamespace(string prefix)
		{
			return this._namespacePrefixTable[prefix];
		}

		public IEnumerable<NamespaceDeclaration> GetNamespacePrefixes()
		{
			return this._namespaces;
		}

		public IEnumerable<AmbientPropertyValue> GetAllAmbientValues(IEnumerable<XamlType> ceilingTypes, bool searchLiveStackOnly, IEnumerable<XamlType> types, params XamlMember[] properties)
		{
			return new AmbientPropertyValue[0];
		}

		public IEnumerable<object> GetAllAmbientValues(params XamlType[] types)
		{
			return new object[0];
		}

		public IEnumerable<AmbientPropertyValue> GetAllAmbientValues(IEnumerable<XamlType> ceilingTypes, params XamlMember[] properties)
		{
			return new AmbientPropertyValue[0];
		}

		public object GetFirstAmbientValue(params XamlType[] types)
		{
			return null;
		}

		public AmbientPropertyValue GetFirstAmbientValue(IEnumerable<XamlType> ceilingTypes, params XamlMember[] properties)
		{
			return null;
		}
	}
}
